/*
 * DataFile.cpp
 *
 *  Created on: 19 juin 2011
 *      Author: incite
 */

#include "DataFile.h"
#include "TagByte.h"
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>

namespace NamedBinaryTag
{
DataFile::DataFile() :
	rootTag(new TagCompound()), compress(true)
{
	rootTag->root = this;
}

DataFile::~DataFile()
{
	if (this->rootTag != NULL)
		delete this->rootTag;
	this->rootTag = NULL;
}

void DataFile::LoadData(std::istream & input, ITag *tag)
{
	TagByte type;
	type.Read(input, true);
	if (type.get_Value() != tag->get_TagType())
		std::__throw_runtime_error(
				__N("DataFile::LoadData: First file element is not a TagCompound."));
	tag->Read(input);
}

void DataFile::SaveData(std::ostream & output, ITag *tag)
{
	TagByte type;
	type.set_Value(this->rootTag->get_TagType());
	type.Write(output, true);
	tag->Write(output);
}

bool DataFile::get_Compress() const
{
	return this->compress;
}

void DataFile::Init()
{
	delete this->rootTag;
	this->rootTag = new TagCompound();
}

TagCompound *DataFile::get_RootTag() const
{
	return this->rootTag;
}

void DataFile::Save(std::ostream & output)
{
	std::ios::iostate state = output.exceptions();
	output.exceptions(std::ios::badbit | std::ios::eofbit | std::ios::failbit);
	try
	{
		if (this->compress)
		{
			boost::iostreams::filtering_ostream foutput;
			foutput.set_auto_close(false);
			foutput.push(boost::iostreams::gzip_compressor());
			foutput.push(output);
			try
			{
				SaveData(foutput, this->rootTag);
				foutput.pop();
			} catch (std::exception )
			{
				foutput.pop();
				throw;
			}
		}
		else
		{
			SaveData(output, this->rootTag);
		}
	} catch (std::exception )
	{
		output.exceptions(state);
		throw;
	}
	output.exceptions(state);
}

void DataFile::Load(std::istream & input)
{
	std::ios::iostate state = input.exceptions();
	input.exceptions(std::ios::badbit | std::ios::eofbit | std::ios::failbit);
	try
	{
		std::auto_ptr<TagCompound> tag(new TagCompound());
		tag->root = this;
		boost::iostreams::filtering_istream finput;
		finput.set_auto_close(false);
		finput.push(boost::iostreams::gzip_decompressor());
		finput.push(input);
		try
		{
			this->LoadData(finput, tag.get());
			finput.pop();
			this->compress = true;
		} catch (boost::iostreams::gzip_error )
		{
			finput.pop();
			input.seekg(0);
			this->LoadData(input, tag.get());
			this->compress = false;
		}
		delete this->rootTag;
		this->rootTag = tag.release();
	} catch (std::exception )
	{
		input.exceptions(state);
		throw;
	}
	input.exceptions(state);
}

void DataFile::set_Compress(bool compress)
{
	this->compress = compress;
}

DataFile::DataFile(const DataFile & other) :
	rootTag(dynamic_cast<TagCompound*>(other.rootTag->clone(this))), compress(other.compress)
{
}

DataFile & DataFile::operator =(DataFile & other)
{
	delete this->rootTag;
	this->rootTag = dynamic_cast<TagCompound*>(other.rootTag->clone(this));
	this->compress = other.compress;
	return *this;
}

}
